#include "app-mysql.h"

#include "app-shared.h"

AppMySql::AppMySql()
{
    m_pMySqlSocket = 0;
    m_sHistTableName = "msghist";

    m_bDbReady = false;
    m_bHistTableReady = false;
}

AppMySql::~AppMySql()
{
    if (m_pMySqlSocket) mysql_close(m_pMySqlSocket);
}

bool AppMySql::connectToDatabase(const char *pchHost,
                                 const char *pchUser, const char *pchPassword,
                                 const char *pchDbName)
{
    // Априорное предположение о неудачном подключении к БД и определении таблицы
    m_bDbReady = false;
    m_bHistTableReady = false;

    // Сохраним имя базы данных
    m_sDbName = pchDbName;

    // Получаем дискриптор соединения с сервром БД
    m_pMySqlSocket = mysql_init(0);
    if (m_pMySqlSocket == 0) {
        IF_ERR_LEVEL
                LG_ERR("[MySQL] Can't create MySQL descriptor");
        return false;
    }

    MYSQL* mres = mysql_real_connect(m_pMySqlSocket,
                                     pchHost,
                                     pchUser, pchPassword,
                                     pchDbName,
                                     0, 0, 0);

    if (mres == 0) {
        IF_ERR_LEVEL
                LG_ERR("[MySQL] Can't connect to MySQL database");
        return false;
    }

    m_bDbReady = true;
    IF_NOTICE_LEVEL
            LG_NOTICE("[MySQL] Successfull connect to MySQL database");

    // Проверим существование таблицы истории в БД
    if (hist_table_exists()) {
        m_bHistTableReady = true;
    } else {
        // Попытка создания требуемой таблицы истории в БД
        m_bHistTableReady = hist_table_create();
    }

    return true;
}

bool AppMySql::query_to_database(const char *pchQuery)
{
    if ( ! m_bDbReady ) return false;

    int ires = mysql_query(m_pMySqlSocket, pchQuery);
    if (ires != 0) {
        IF_ERR_LEVEL
                LV_ERR("[MySQL] Can't execute MySQL query: %s", pchQuery);
        return false;
    }
    return true;
}

/**
  Выполнение SELECT для всех полей таблицы истории

  @param vec - вектор кортежей, возвращаемых по запросу
  @return true, при успешной обработке запроса.

  @todo Заменить atoi() на собственные эффективные функции и избавиться от stdlib.h
  */
bool AppMySql::hist_table_select(std::vector<HistRow> &vec, const char *pchSelectQuery)
{
    if ( ! m_bHistTableReady) return false;

    std::string sQuery;
    if (pchSelectQuery == 0) {
        sQuery = "SELECT * FROM ";
        sQuery += m_sHistTableName;
    }  else {
        sQuery = pchSelectQuery;
    }
    int ires = mysql_query(m_pMySqlSocket, sQuery.c_str());

    if (ires != 0) {
        IF_ERR_LEVEL
                LV_ERR("[MySQL] Can't execute MySQL query: %s", sQuery.c_str());
        return false;
    }

    MYSQL_RES *res = mysql_store_result(m_pMySqlSocket);
    if (res == 0) {
        IF_ERR_LEVEL
                LV_ERR("[MySQL] Can't get MYSQL_RES descriptor "
                       "to process result for: %s", sQuery.c_str());
        return false;
    }

    MYSQL_ROW row;
    int size = mysql_num_rows(res);

    if (size > 0) {
        while ((row = mysql_fetch_row(res)) != 0) {
            HistRow hr;
            hr.src = atoi(row[1]);
            hr.dest = atoi(row[2]);
            hr.msg  = row[3];
            vec.push_back(hr);
        }
    }
    mysql_free_result(res);

    return true;
}

bool AppMySql::hist_table_insert(HistRow &hr)
{
   if ( ! m_bHistTableReady) return false;

   std::stringstream ss;
   ss << "insert into msghist (src, dest, msg) values (" << hr.src <<
         ", " << hr.dest << ", \"" << hr.msg <<"\" )";
   return query_to_database(ss.str().c_str());
}

bool AppMySql::hist_table_create()
{
    std::stringstream ss;
    ss << "create table " << m_sHistTableName << " (";
    ss << "id int not null auto_increment, ";
    ss << "src int, ";
    ss << "dest int, ";
    ss << "msg varchar(255), ";
    ss << "primary key(id) ) default charset=utf8";

    bool bRes = query_to_database(ss.str().c_str());
    if (bRes) {
        IF_NOTICE_LEVEL
                LG_NOTICE("[MySQL] History table has created successfully");
    } else {
        IF_ERR_LEVEL
                LV_ERR("[MySQL] Can't create a history table. "
                       "Error in query: %s", ss.str().c_str());
    }

    return bRes;
}

bool AppMySql::hist_table_drop()
{
    std::stringstream ss;
    ss << "drop table " << m_sHistTableName;

    bool bRes = query_to_database(ss.str().c_str());
    if (bRes) {
        IF_NOTICE_LEVEL
                LG_NOTICE("[MySQL] History table has dropped successfully");
    } else {
        IF_ERR_LEVEL
                LV_ERR("[MySQL] Can't drop a history table. "
                       "Error in query: %s", ss.str().c_str());
    }

    return bRes;
}

bool AppMySql::hist_table_exists()
{        
    std::string sQuery = "SHOW TABLES";
    bool bRes = query_to_database(sQuery.c_str());
    if ( ! bRes ) return false;

    MYSQL_RES *res = mysql_store_result(m_pMySqlSocket);
    if (res == 0) {
        IF_ERR_LEVEL
                LV_ERR("[MySQL] Can't get MYSQL_RES descriptor "
                       "to process result for: %s", sQuery.c_str());
        return false;
    }

    MYSQL_ROW row;
    int size = mysql_num_rows(res);

    bool bFound = false;
    if (size > 0) {
        while ((row = mysql_fetch_row(res)) != 0) {
            std::string s = row[0];
            if (s == m_sHistTableName) {
                IF_NOTICE_LEVEL
                        LV_NOTICE("[MySQL] History table '%s' exists in DB",
                                  m_sHistTableName.c_str());
                bFound = true;
                break;
            }
        }
    }
    mysql_free_result(res);

    return bFound;
}

void AppMySql::hist_table_fill_row(HistRow &hr,
                                    int src, int dest, const char *pchMsg)
{
    hr.src = src;
    hr.dest = dest;
    hr.msg = pchMsg;
}
